package edu.mercer.sse.tests.web;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.junit.Before;
import org.junit.After;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import edu.mercer.sse.tasks.web.DownloadURLFunction;
import edu.mercer.sse.tasks.web.URLRetrieval;

/**
 * JUnit test of ThreadProxy for the StringBuilder Object type.<p>
 * 
 * Test Thread Object Proxy Implementation using concurrent execution.<p>
 * 
 * Test thread-global storage for multiple threads using multiple Callable 
 * 	class instances.<p>
 */

public class TestMultiThread
{
	private final static Integer DOWNLOAD_TASKS_COUNT = 3;

	/** One thread for each concurrent download task.<p> */
	
	final ExecutorService _threadEx = Executors.newCachedThreadPool();

	private final static String KNOWN_WEBSITE = "http://medapps.mercer.edu";
	
	private static Integer _knownwebsiteSize = 0;	
	
	
	/**
	 * Download contents from known, existing website.<p>
	 * 
	 * Retrieve size for use in test.<p>
	 */
	
	@Before
	public void setUp()
	{
		_knownwebsiteSize = new DownloadURLFunction().apply( KNOWN_WEBSITE )
				.length();
		
		assertTrue( _knownwebsiteSize > 0 );
		
	}//method	
	

	/**
	 * Test thread-global storage.<p>
	 * 
	 * Download contents of URL's. Ensure contents are NOT appended to 
	 * 	thread-global StringBuilder instance(s) as separate threads are used.
	 * <p>
	 * 
	 * @throws InterruptedException - thread interrupted
	 * 
	 * @throws ExecutionException - URLConnection, Security Manager or other 
	 * 	errors wrapped
	 */
	
	@Test
	public void test() throws InterruptedException, 
							  ExecutionException
	{
		assertTrue( "zero: total number of tasks defined", 
				DOWNLOAD_TASKS_COUNT > 0 );
		
		final ArrayList<URLRetrieval> tasks = new ArrayList<>();
		
		/* 
		 * output should NOT be appended to single thread-global StringBuilder 
		 * with key of '1'
		 * 
		 * output should be appended to unique StringBuilder instance for 
		 * 	each thread
		 */

		for( Integer ct=0; ct < DOWNLOAD_TASKS_COUNT; ct++  )
		{ 
			tasks.add( new URLRetrieval(KNOWN_WEBSITE, 1) );
		
		}//for
		
		final List<Future<String>> taskResults = _threadEx.invokeAll(tasks);		

		/* 
		 * allow tasks to complete concurrently: avoid using .get() upon 
		 * 	Futures as might causes one thread to be reused and run 
		 * 	sequentially 
		 */
		
		while( true )
		{
			Boolean stillRunning = false;

			for( Integer cx=0; cx < taskResults.size(); cx++ )
			{
				if( ! taskResults.get(cx).isDone() )
				{ 
					stillRunning = true;
					break;
				
				}//if
				
			}//for

			if( !stillRunning ) break;

			Thread.sleep(1000L);
			
		}//while		
		
		for( Integer cx=0; cx < taskResults.size(); cx++ )
		{
			assertEquals( "size unexpected for download task: " 
					+ 
					cx, 

					_knownwebsiteSize,

					new ParseSizeFunction().apply( taskResults.get(cx)
							.get()));
			
		}//for
		
	}//method

	
	/**
	 * Shutdown executor service.<p>
	 */
	
	@After
	public void cleanUp()
	{
		_threadEx.shutdown();
		
	}//method

	
}//class
